/*
 * This file is part of the SDWebImage package.
 * (c) Olivier Poitrey <rs@dailymotion.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

#import <Foundation/Foundation.h>
#import <libkern/OSAtomic.h>
#import <os/lock.h>

#import "SDmetamacros.h"

#define SD_USE_OS_UNFAIR_LOCK                                                                                                                                                            \
    TARGET_OS_MACCATALYST || (__IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_10_0) || (__MAC_OS_X_VERSION_MIN_REQUIRED >= __MAC_10_12) || (__TV_OS_VERSION_MIN_REQUIRED >= __TVOS_10_0) || \
        (__WATCH_OS_VERSION_MIN_REQUIRED >= __WATCHOS_3_0)

#ifndef SD_LOCK_DECLARE
#if SD_USE_OS_UNFAIR_LOCK
#define SD_LOCK_DECLARE(lock) os_unfair_lock lock
#else
#define SD_LOCK_DECLARE(lock)                                                         \
    os_unfair_lock lock API_AVAILABLE(ios(10.0), tvos(10), watchos(3), macos(10.12)); \
    OSSpinLock lock##_deprecated;
#endif
#endif

#ifndef SD_LOCK_DECLARE_STATIC
#if SD_USE_OS_UNFAIR_LOCK
#define SD_LOCK_DECLARE_STATIC(lock) static os_unfair_lock lock
#else
#define SD_LOCK_DECLARE_STATIC(lock) static os_unfair_lock lock API_AVAILABLE(ios(10.0), tvos(10), watchos(3), macos(10.12)); \
static OSSpinLock lock##_deprecated;
#endif
#endif

#ifndef SD_LOCK_INIT
#if SD_USE_OS_UNFAIR_LOCK
#define SD_LOCK_INIT(lock) lock = OS_UNFAIR_LOCK_INIT
#else
#define SD_LOCK_INIT(lock)                                      \
    if (@available(iOS 10, tvOS 10, watchOS 3, macOS 10.12, *)) \
        lock = OS_UNFAIR_LOCK_INIT;                             \
    else                                                        \
        lock##_deprecated = OS_SPINLOCK_INIT;
#endif
#endif

#ifndef SD_LOCK
#if SD_USE_OS_UNFAIR_LOCK
#define SD_LOCK(lock) os_unfair_lock_lock(&lock)
#else
#define SD_LOCK(lock)                                           \
    if (@available(iOS 10, tvOS 10, watchOS 3, macOS 10.12, *)) \
        os_unfair_lock_lock(&lock);                             \
    else                                                        \
        OSSpinLockLock(&lock##_deprecated);
#endif
#endif

#ifndef SD_UNLOCK
#if SD_USE_OS_UNFAIR_LOCK
#define SD_UNLOCK(lock) os_unfair_lock_unlock(&lock)
#else
#define SD_UNLOCK(lock)                                         \
    if (@available(iOS 10, tvOS 10, watchOS 3, macOS 10.12, *)) \
        os_unfair_lock_unlock(&lock);                           \
    else                                                        \
        OSSpinLockUnlock(&lock##_deprecated);
#endif
#endif

#ifndef SD_OPTIONS_CONTAINS
#define SD_OPTIONS_CONTAINS(options, value) (((options) & (value)) == (value))
#endif

#ifndef SD_CSTRING
#define SD_CSTRING(str) #str
#endif

#ifndef SD_NSSTRING
#define SD_NSSTRING(str) @(SD_CSTRING(str))
#endif

#ifndef SD_SEL_SPI
#define SD_SEL_SPI(name) NSSelectorFromString([NSString stringWithFormat:@"_%@", SD_NSSTRING(name)])
#endif

#ifndef weakify
#define weakify(...) jx_keywordify metamacro_foreach_cxt(jx_weakify_, , __weak, __VA_ARGS__)
#endif

#ifndef strongify
#define strongify(...) jx_keywordify _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wshadow\"") metamacro_foreach(jx_strongify_, , __VA_ARGS__) _Pragma("clang diagnostic pop")
#endif

#define jx_weakify_(INDEX, CONTEXT, VAR) CONTEXT __typeof__(VAR) metamacro_concat(VAR, _weak_) = (VAR);

#define jx_strongify_(INDEX, VAR) __strong __typeof__(VAR) VAR = metamacro_concat(VAR, _weak_);

#if DEBUG
#define jx_keywordify \
    autoreleasepool {}
#else
#define jx_keywordify \
    try {             \
    } @catch (...) {  \
    }
#endif

#ifndef onExit
#define onExit jx_keywordify __strong jx_cleanupBlock_t metamacro_concat(jx_exitBlock_, __LINE__) __attribute__((cleanup(jx_executeCleanupBlock), unused)) = ^
#endif

typedef void (^jx_cleanupBlock_t)(void);

#if defined(__cplusplus)
extern "C" {
#endif
void jx_executeCleanupBlock(__strong jx_cleanupBlock_t *block);
#if defined(__cplusplus)
}
#endif
